package org.mx.test.service;

import org.mx.StringUtils;
import org.mx.dal.EntityFactory;
import org.mx.dal.service.GeneralDictAccessor;
import org.mx.error.UserInterfaceSystemErrorException;
import org.mx.test.entity.PrivilegeEntity;
import org.mx.test.entity.RoleEntity;
import org.mx.test.entity.UserEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@SuppressWarnings("javadoc")
@Service("rbacService")
@CacheConfig(cacheNames = {"rbac-cache"})
public class RbacServiceImpl implements RbacService {
    private static final Logger logger = LoggerFactory.getLogger(RbacServiceImpl.class);

    private GeneralDictAccessor dictAccessor;

    @Autowired
    public RbacServiceImpl(@Qualifier("generalDictAccessor") GeneralDictAccessor dictAccessor) {
        super();
        this.dictAccessor = dictAccessor;
    }

    @Transactional(readOnly = true)
    @Override
    public UserBean getUserRbacInfo(String userCode) {
        if (StringUtils.isBlank(userCode)) {
            if (logger.isErrorEnabled()) {
                logger.error("The user's code is blank.");
            }
            throw new UserInterfaceSystemErrorException(
                    UserInterfaceSystemErrorException.SystemErrors.SYSTEM_ILLEGAL_PARAM
            );
        }
        UserEntity userEntity = dictAccessor.getByCode(userCode, UserEntity.class);
        if (userEntity == null) {
            if (logger.isWarnEnabled()) {
                logger.warn(String.format("The user[%s] not found.", userCode));
            }
            return null;
        }
        UserBean userBean = new UserBean();
        userBean.setCode(userEntity.getCode());
        userBean.setId(userEntity.getId());
        userBean.setName(userEntity.getName());
        Set<RoleEntity> roles = userEntity.getRoles();
        if (roles != null && !roles.isEmpty()) {
            Set<String> roleCodes = new HashSet<>();
            Set<String> privilegeCodes = new HashSet<>();
            roles.forEach(role -> {
                roleCodes.add(role.getCode());
                Set<PrivilegeEntity> privileges = role.getPrivileges();
                if (privileges != null && !privileges.isEmpty()) {
                    privileges.forEach(privilege -> privilegeCodes.add(privilege.getCode()));
                }
            });
            userBean.setRoleCodes(new ArrayList<>(roleCodes));
            userBean.setPrivilegeCodes(new ArrayList<>(privilegeCodes));
        }
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Get all the privileges by user's code: %s.", userCode));
        }
        return userBean;
    }

    @Transactional(readOnly = true)
    @Override
    @Cacheable(key = "#userCode")
    public UserBean getUserRbacInfoByCache(String userCode) {
        return getUserRbacInfo(userCode);
    }

    @CacheEvict(key = "#userCode")
    @Override
    public void cleanCache(String userCode) {
        // do nothing
        if (logger.isInfoEnabled()) {
            logger.info(String.format("Clean the cache by user's code: %s.", userCode));
        }
    }

    @CacheEvict(allEntries = true)
    @Override
    public void cleanCache() {
        // do nothing
        if (logger.isInfoEnabled()) {
            logger.info("Clean all the cache.");
        }
    }

    @Transactional
    @Override
    @CacheEvict
    public void initRbacData() {
        // init privilege
        String[] privilegeCodes = {"p1", "p2", "p3", "p4"};
        PrivilegeEntity[] privileges = new PrivilegeEntity[4];
        int index = 0;
        for (String code : privilegeCodes) {
            PrivilegeEntity p = EntityFactory.createEntity(PrivilegeEntity.class);
            p.setCode(code);
            p.setName(code + " (name)");
            privileges[index ++] = dictAccessor.save(p);
        }

        // init role
        String[] roleCodes = {"admin", "user"};
        for (String code : roleCodes) {
            RoleEntity r = EntityFactory.createEntity(RoleEntity.class);
            r.setCode(code);
            r.setName(code + " (name)");
            dictAccessor.save(r);
        }
        RoleEntity admin = dictAccessor.getByCode("admin", RoleEntity.class);
        admin.setPrivileges(new HashSet<>(Arrays.asList(privileges)));
        dictAccessor.save(admin);
        RoleEntity user = dictAccessor.getByCode("user", RoleEntity.class);
        user.setPrivileges(new HashSet<>(Arrays.asList(privileges[0], privileges[2])));
        dictAccessor.save(user);

        // init user
        String[] userCodes = {"josh", "john", "joy"};
        for (String code : userCodes) {
            UserEntity u = EntityFactory.createEntity(UserEntity.class);
            u.setCode(code);
            u.setName(code + " (name)");
            dictAccessor.save(u);
        }
        UserEntity josh = dictAccessor.getByCode("josh", UserEntity.class);
        josh.setRoles(new HashSet<>(Collections.singletonList(admin)));
        dictAccessor.save(josh);
        UserEntity john = dictAccessor.getByCode("john", UserEntity.class);
        john.setRoles(new HashSet<>(Arrays.asList(admin, user)));
        dictAccessor.save(john);
        UserEntity joy = dictAccessor.getByCode("joy", UserEntity.class);
        joy.setRoles(new HashSet<>(Collections.singletonList(user)));
        dictAccessor.save(joy);

        if (logger.isDebugEnabled()) {
            logger.debug("Initialize all the privileges for user successfully.");
        }
    }
}
